iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
0
Modern Web

前端常見問題攻略系列 第 12

學好 this 前,先搞清楚 this 做什麼

  • 分享至 

  • xImage
  •  

過去到現在寫了很多 this 相關的文章,如果沒有實際運用過 this 的開發者可能會有疑惑「this 是不是只有在面試考題用到?」、「this 在實戰中會用到嗎?」、「為什麼要學 this?」。

其實 this 在實戰中的應用非常廣,已經有接觸框架的開發者對此較不陌生,基本上所有的檔案都會存在 this,而本篇也就針對 this 的應用情境進行介紹。

沒有使用元件的開發

過去框架尚未盛行以前,開發上都是平鋪直述的概念在進行,當有一個需求如下時:

  1. 建立資料
  2. 將資料取出並繪製於畫面上
function displayData(data, target) {
  console.log(data.string);
  console.log(`將 ${ data.string } 繪製於 ${ target } 上`)
}
var someData = {
  string: '用在卡片上的資料',
  target: '卡片'
}
displayData(someData, '卡片');

透過這樣的方式,我們定義了 資料、目標位置、渲染用的函式,此階段中沒有元件的概念,彼此之間再結構上沒有任何關聯性,當程式碼較少時相當好閱讀,但在日趨複雜的情況就可能會有以下的狀況:

  • 函式與資料分離,函式的方法不一定能夠物件內資料
  • 網站越來越複雜時,資料與函式可能散落在不同處
  • 函式命名可能會產生衝突

導入物件的概念

接下來導入物件的概念以後,可以將方法與資料整合在一起,這樣就可以解決上述所列出的問題,不僅可以避免資料與方法分離的狀況,命名上也可以更為簡易。

var card = {
  data: '卡片上的資料',
  target: '卡片',
  render: function() {
    console.log(`這個是 ${this.data},可以繪製於 ${this.target}`);
    // 這個元件是 卡片上的資料,可以繪製於 卡片
  },

  trigger: function() {
    this.render();
  }
}
card.trigger();

當透過物件的形式進行開發時,就需要使用 this 來取得或呼叫物件中彼此之間的方法與資料。如果對於 this 觀念想補強可參考此文章

https://ithelp.ithome.com.tw/upload/images/20200927/20083608zw4bNxIj5S.png

額外問題:偏不使用 this,改用 card 不行嗎?

這也是同學們常見的問題,在此範例中物件內的 this 等同於 card 物件,所以將上述程式碼中的 this 均替換為 card 也是可以運作的。

var card = {
  data: '卡片上的資料',
  target: '卡片',
  render: function () {
    console.log(`這個是 ${card.data},可以繪製於 ${card.target}`);
    // 這個元件是 卡片上的資料,可以繪製於 卡片
  },

  trigger: function () {
    card.render();
  },
};
card.trigger();

不過實戰中,以物件的方式建構元件時,會盡可能地增加其使用性,所以 card 在原始碼中並不會是唯一的存在,可能會有更多的相同或類似的結構來作為運用。

如以下的物件可以延伸自 card 元件,如果使用同樣的名稱就會導致運作上的錯誤,維持 this 將是最好的做法。

var navbar = {
  data: '導覽列的資料',
	target: '導覽列',
  render: function() {
    console.log(`這個是 ${this.data},可以繪製於 ${this.target}`)
  },

  trigger: function() {
    this.render();
  }
}
navbar.trigger();

而實際上是如何建立兩個接近結構的元件呢?

以目前來說兩者的結構是接近的,只有 data 的內容是不同的,這段在實戰中大多都是藉由 “框架” 的特性來完成,以下會繼續介紹框架是如何做到這樣的結構,並且如何管理物件中相同的方法。

https://ithelp.ithome.com.tw/upload/images/20200927/20083608ZAYQWD3V3r.png

框架、原型與 this

為了讓各個物件中會重複使用的方法統一管理,並且讓其又能定義屬於各自的方法,框架中大多會使用 JavaScript 中的一特性 “Prototype” 來進行管理,關於 Prototype 的觀念本篇不會多加敘述,如需了解可參考:

  1. 原型繼承:https://wcc723.github.io/javascript/2017/12/17/javascript-prototype/
  2. 建構函式:https://wcc723.github.io/javascript/2017/12/18/javascript-constructor/

透過原型可以將物件中的方法集中定義,並且使兩者均可透過 this.render() 的方式呼叫此方法。

https://ithelp.ithome.com.tw/upload/images/20200927/200836086HHvUUbeGP.png

需要定義原型,則必須運用到 “建構函式”,以下提供的建構函式範例可用來建立如上結構的物件,並且將 render 的方法預先定義為原型,而建構函式在此的目的為:

  • 定義物件的結構(方法會先放在 methods 內,此結構與 Vue 是接近的)
  • 將共用的方法使用原型來定義
function Component(obj) {
  var vm = this;

  // 取出 methods 屬性內的物件,定義為各自的方法
  var methods = Object.keys(obj.methods);
  if (methods.length) {
    methods.forEach(function(key) {
      vm[key] = obj.methods[key];
    });
  }
  vm.data = obj.data;
  vm.target = obj.target;
}

Component.prototype.render = function() {
  console.log(`這個是 ${this.data},可以繪製於 ${this.target}`);
}

接下來,就可以透過上方定義的建構函式來建立相同的物件。

var navbar = new Component({
  data: '導覽列的資料',
  target: '導覽列',
  methods: {
    trigger: function() {
      this.render();
    }
  }
});
navbar.trigger(); // 這個是 導覽列的資料,可以繪製於 導覽列

此結構透過 Chrome Console 可看到類似於先前所列的結構,僅不過將 render 改為在原型內,呼叫方式也一樣是使用 this

https://ithelp.ithome.com.tw/upload/images/20200927/20083608w3X71bnRRA.png

var card = new Component({
  data: '卡片的資料',
  target: '卡片',
  methods: {
    trigger: function() {
      console.log(`${this.data} 在這`); // 卡片的資料 在這
    }
  }
});
card.trigger();

透過建構函式就可不斷建立相同結構的物件,也可定義屬於各自的資料、方法,而物件之中都依然是使用 this 來進行該物件內的所有資料、方法的調用。也因此 this 是一個取得本地元件屬性的方法,如果搞清楚為何使用,也能夠發現其實它並沒有那麼複雜(觀念非常多,但實戰中只會用到一招而已)。


上一篇
閉包,原來這就是閉包啊!
下一篇
透過練習題,摸熟 This 的運作
系列文
前端常見問題攻略30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言